/*************************************************************************
 *
 *   file		: ObjectPool.cs
 *   copyright		: (C) The WCell Team
 *   email		: info@wcell.org
 *   last changed	: $LastChangedDate: 2009-09-01 07:52:17 +0100 (Tue, 01 Sep 2009) $
 *   last author	: $LastChangedBy: dominikseifert $
 *   revision		: $Rev: 1061 $
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *************************************************************************/

using System;
using Cell.Core.Collections;
using System.Threading;

#pragma warning disable 0420

namespace Cell.Core
{
	/// <summary>
	/// A structure that contains information about an object pool.
	/// </summary>
	public struct ObjectPoolInfo
	{
		/// <summary>
		/// The number of hard references contained in the pool.
		/// </summary>
		public int HardReferences;

		/// <summary>
		/// The number of weak references contained in the pool.
		/// </summary>
		public int WeakReferences;


		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="weak">The number of weak references in the pool.</param>
		/// <param name="hard">The number of hard references in the pool.</param>
		public ObjectPoolInfo(int weak, int hard)
		{
			HardReferences = hard;
			WeakReferences = weak;
		}
	}

	/// <summary>
	/// This class represents a pool of objects.
	/// </summary>
	public class ObjectPool<T> : IObjectPool where T : class
	{
		/// <summary>
		/// Delegate holds pointers to functions for allocating new pool objects.
		/// </summary>
		public delegate T ObjCreateFunc();

		/// <summary>
		/// A queue of objects in the pool.
		/// </summary>
		private LockfreeQueue<object> _queue = new LockfreeQueue<object>();

		/// <summary>
		/// The minimum # of hard references that must be in the pool.
		/// </summary>
		private volatile int _minSize = 25;

		/// <summary>
		/// The number of hard references in the queue.
		/// </summary>
		private volatile int _hardRef = 0;

		/// <summary>
		/// Function pointer to the allocation function.
		/// </summary>
		private ObjCreateFunc _createObj;

		/// <summary>
		/// Gets the number of hard references that are currently in the pool.
		/// </summary>
		public int HardReferenceCount
		{
			get { return _hardRef; }
		}

		/// <summary>
		/// Gets the minimum size of the pool.
		/// </summary>
		public int MinimumSize
		{
			get { return _minSize; }
			set { _minSize = value; }
		}

		/// <summary>
		/// Gets information about the object pool.
		/// </summary>
		/// <value>A new <see cref="ObjectPoolInfo"/> object that contains information about the pool.</value>
		public ObjectPoolInfo Info
		{
			get
			{
				ObjectPoolInfo info;
				info.HardReferences = _hardRef;
				info.WeakReferences = _queue.Count - _hardRef;

				return info;
			}
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="func">Function pointer to the allocation function.</param>
		public ObjectPool(ObjCreateFunc func)
		{
			_createObj = func;
		}

		/// <summary>
		/// Adds an object to the queue.
		/// </summary>
		/// <param name="obj">The object to be added.</param>
		/// <remarks>If there are at least <see cref="ObjectPool&lt;T&gt;.MinimumSize"/> hard references in the pool then the object is added as a WeakReference.
		/// A WeakReference allows an object to be collected by the GC if there are no other hard references to it.</remarks>
		public void Enqueue(T obj)
		{
			if (obj is IPooledObject)
			{
				((IPooledObject)obj).Cleanup();
			}

			if (_hardRef >= _minSize)
			{
				_queue.Enqueue(new WeakReference(obj));
			}
			else
			{
				_queue.Enqueue(obj);
				Interlocked.Increment(ref _hardRef);
			}
		}

		/// <summary>
		/// Adds an object to the queue.
		/// </summary>
		/// <param name="obj">The object to be added.</param>
		/// <remarks>If there are at least <see cref="ObjectPool&lt;T&gt;.MinimumSize"/> hard references in the pool then the object is added as a WeakReference.
		/// A WeakReference allows an object to be collected by the GC if there are no other hard references to it.</remarks>
		public void Enqueue(object obj)
		{
			if (obj is T)
			{
				if (obj is IPooledObject)
				{
					((IPooledObject)obj).Cleanup();
				}

				if (_hardRef >= _minSize)
				{
					_queue.Enqueue(new WeakReference(obj));
				}
				else
				{
					_queue.Enqueue(obj);
					Interlocked.Increment(ref _hardRef);
				}
			}
		}

#pragma warning disable 0693
		/// <summary>
		/// Removes an object from the queue.
		/// </summary>
		/// <returns>An object from the queue or a new object if none were in the queue.</returns>

		public T Dequeue()
		{
			object obj;

		DequeueObj:
			{
				if (!_queue.TryDequeue(out obj))
				{
					return _createObj();
				}

				if (obj is WeakReference)
				{
					WeakReference robj = (WeakReference)obj;
					if (robj.IsAlive)
					{
						return robj.Target as T;
					}
				    goto DequeueObj;
				}

			    Interlocked.Decrement(ref _hardRef);
			    return obj as T;
			}
		}
#pragma warning restore 0693

		/// <summary>
		/// Removes an object from the queue.
		/// </summary>
		/// <returns>An object from the queue or a new object if none were in the queue.</returns>
		public object DequeueObj()
		{
			object obj;

		DequeueObj:
			{
				if (!_queue.TryDequeue(out obj))
				{
					return _createObj();
				}

				WeakReference robj = obj as WeakReference;
				if (robj != null)
				{
				    if (robj.IsAlive)
					{
						return robj.Target;
					}
				    goto DequeueObj;
				}
			    Interlocked.Decrement(ref _hardRef);
			}

			return obj;
		}
	}
}

#pragma warning restore 0420